package godbv2

import (
	"context"
	"database/sql"
	"errors"
	"time"
)

type DriverName string

type ConnectQuery struct {
	Conn         QueryWithContext //这里提取了sql.DB以及sql.Tx的公共方法使得该结构体可以不区分是DB还是Tx
	CheckConn    bool
	Dbname       string
	DriverName   DriverName
	QueryTimeOut time.Duration
}

func (q *ConnectQuery) Connect() error {
	if q.Conn == nil {
		return DatabaseNotOpen
	}
	if ping, ok := q.Conn.(*sql.DB); ok {
		if err := ping.Ping(); err != nil {
			return ErrorPing
		}
	}
	return nil
}

func (q *ConnectQuery) QueryRowsWithContext(ctx context.Context, sql string, args ...any) *QueryResult {
	if q.CheckConn {
		if err := q.Connect(); err != nil {
			return ErrQueryResult(err, q.Dbname, sql, args)
		}
	}
	if q.QueryTimeOut > 0 {
		newCtx, cancelFun := context.WithTimeout(ctx, q.QueryTimeOut)
		ctx = newCtx
		defer cancelFun()
	}
	rows, err := q.Conn.QueryContext(ctx, sql, args...)
	if err != nil {
		return ErrQueryResult(err, q.Dbname, sql, args)
	}
	return NewRowsResult(rows, sql, args)
}

func (q *ConnectQuery) ExecWithContext(ctx context.Context, sql string, args ...any) *ExecResult {
	if q.CheckConn {
		if err := q.Connect(); err != nil {
			return ErrExecResult(err, q.Dbname, sql, args)
		}
	}
	if q.QueryTimeOut > 0 {
		newCtx, cancelFun := context.WithTimeout(ctx, q.QueryTimeOut)
		ctx = newCtx
		defer cancelFun()
	}
	result, err := q.Conn.ExecContext(ctx, sql, args...)
	if err != nil {
		return ErrExecResult(err, q.Dbname, sql, args)
	}
	return NewExecResult(result, sql, args)
}

func (q *ConnectQuery) Prepare(query string) (*sql.Stmt, error) {
	if q.CheckConn {
		if err := q.Connect(); err != nil {
			return nil, err
		}
	}
	return q.Conn.Prepare(query)
}

func (q *ConnectQuery) QueryRows(sql string, args ...interface{}) *QueryResult {
	return q.QueryRowsWithContext(context.Background(), sql, args...)
}

func (q *ConnectQuery) Exec(sql string, args ...interface{}) *ExecResult {
	return q.ExecWithContext(context.Background(), sql, args...)
}

func (q *ConnectQuery) Transaction(t TransactionFunc, option ...*sql.TxOptions) error {
	return errors.New("transaction not supported")
}

// SetQueryTimeOut 设置查询超时时间
func (q *ConnectQuery) SetQueryTimeOut(outTime time.Duration) {
	q.QueryTimeOut = outTime
}

// DataBaseName 数据库名称
func (q *ConnectQuery) DataBaseName() string {
	if q == nil || q.Dbname == "" {
		return ""
	}
	return q.Dbname
}

// SetDataBaseName 设置数据库名称
func (q *ConnectQuery) SetDataBaseName(dbname string) {
	q.Dbname = dbname
}

type txQuery interface {
	Query
	// SetConn 设置链接对象
	SetConn(conn QueryWithContext)
}

type TxConnectQuery struct {
	txQuery
	tx  *sql.Tx
	err error
}

func NewTxConnectQuery(conn txQuery) *TxConnectQuery {
	return &TxConnectQuery{
		txQuery: conn,
	}
}

func (m *TxConnectQuery) GetTxSQL(tx *sql.Tx) TxSQL {
	m.tx = tx
	m.SetConn(m.tx)
	return m
}

func (m *TxConnectQuery) GetTx() *sql.Tx {
	return m.tx
}

// Err 返回执行错误
func (m *TxConnectQuery) Err() error {
	return m.err
}

func (m *TxConnectQuery) Exec(sql string, args ...interface{}) *ExecResult {
	if m.err != nil { //前面查询已经出错，会导致mssql直接回滚事务，所以后续的exec执行不应提交否则后续exec不受事务管控
		return ErrExecResult(errors.New("前面执行已出错中断后续操作"), m.DataBaseName(), sql, args)
	}
	res := m.txQuery.Exec(sql, args...)
	m.err = res.Error()
	return res
}

// Transaction 事务中再调事务不再开启新事务，而是沿用原事务
func (m *TxConnectQuery) Transaction(t TransactionFunc, option ...*sql.TxOptions) error {
	if m.err != nil {
		return m.err
	}
	m.err = t(m)
	return m.err
}

type Connect struct {
	db *sql.DB
}

// SetSQLDB 设置数据库链接
func (c *Connect) SetSQLDB(dbSQL *sql.DB) {
	c.db = dbSQL
}

// NewTransaction 事务处理
// @param t TransactionFunc 事务处理函数
func (c *Connect) NewTransaction(t TransactionFunc, newTx func(tx *sql.Tx) TxSQL, option ...*sql.TxOptions) error {
	if t == nil {
		return nil
	} else if newTx == nil {
		return ErrorTx
	} else if c.db == nil {
		return DatabaseNotOpen
	} else if err := c.db.Ping(); err != nil {
		return ErrorPing
	}
	var txOption *sql.TxOptions
	if len(option) > 0 && option[0] != nil {
		txOption = option[0]
	}
	tx, err := c.db.BeginTx(context.Background(), txOption)
	if err == nil {
		defer func() {
			if err := recover(); err != nil {
				//发生异常,先回滚事务再继续抛出异常
				tx.Rollback() //回滚
				log.Error("事务崩溃", "error", err)
			}
		}()
		ntx := newTx(tx)
		if ntx == nil {
			tx.Rollback()
			return ErrorTx
		}
		e := t(ntx)
		if e != nil {
			tx.Rollback()
			return e
		}
		// 执行过程中存在错误,回滚
		if etx, ok := ntx.(TxSQLErr); ok {
			terr := etx.Err()
			if terr != nil {
				tx.Rollback()
				return terr
			}
		}
		err = tx.Commit()
		if err != nil { //事务提交失败,回滚事务,返回错误
			tx.Rollback()
		}
	}
	return err
}

// GetDb 获取数据库对象
func (c *Connect) GetDb() (*sql.DB, error) {
	if c.db == nil {
		return nil, DatabaseNotOpen
	} else if err := c.db.Ping(); err != nil {
		return nil, ErrorPing
	}
	return c.db, nil
}

// SetMaxOpenConns 设置最大连接数
func (c *Connect) SetMaxOpenConns(n int) {
	c.db.SetMaxOpenConns(n)
}

// SetMaxIdleConns 设置最大空闲连接数
func (c *Connect) SetMaxIdleConns(n int) {
	c.db.SetMaxIdleConns(n)
}

// SetConnMaxIdleTime 设置最大空闲连接时间,超时后将关闭连接
func (c *Connect) SetConnMaxIdleTime(n time.Duration) {
	c.db.SetConnMaxIdleTime(n)
}

// Close 关闭连接
func (c *Connect) Close() {
	if c.db != nil {
		c.db.Close()
		c.db = nil
	}
}
